// 版权所有2009年围棋作者。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

// 该文件实现了AST节点的打印；明确地
// 表达式、语句、声明和文件。它使用
// printer.go中实现的打印功能。

package printer

import (
	"bytes"
	"go/ast"
	"go/internal/typeparams"
	"go/token"
	"math"
	"strconv"
	"strings"
	"unicode"
	"unicode/utf8"
)

// 格式问题：
// -更好的注释格式，适用于行末的/*-样式的注释（例如声明）
// 当注释跨越多行时；如果这样的注释只有两行，则格式设置是错误的
// 非幂等的
// -表达式列表的格式
// -应使用空白而不是制表符来分隔单行函数体和
// 函数标题，除非有一组连续的单行线

// ----------------------------------------------------------------------------
// 公共AST节点。

// 根据需要打印尽可能多的换行符（但至少要打印最少的换行符），以便
// 当前行。ws在第一个换行符之前打印。如果新闻节
// 设置后，第一个换行符作为formfeed打印。如果没有行，则返回0
// 已打印换行符，如果恰好打印了一个换行符，则返回1，
// 如果有一个formfeed或多个换行符，则返回一个大于1的值
// 印刷的。
// None
// TODO（gri）：如果下一条语句位于“line”处，linebreak可能会添加太多行
// 前面有注释，因为n的计算假定
// 注释前的当前位置和目标位置
// 在评论之后。因此，在穿插了这样的评论之后
// 它们占用的空间不被视为减少
// 换行符。目前还没有简单的方法来了解
// 此函数中的未来（尚未散布）注释。
// None
func (p *printer) linebreak(line, min int, ws whiteSpace, newSection bool) (nbreaks int) {
	n := nlimit(line - p.pos.Line)
	if n < min {
		n = min
	}
	if n > 0 {
		p.print(ws)
		if newSection {
			p.print(formfeed)
			n--
			nbreaks = 2
		}
		nbreaks += n
		for ; n > 0; n-- {
			p.print(newline)
		}
	}
	return
}

// 如果g！=setComment，则setComment将g设置为下一条注释nil和if节点注释
// 已启用-打印源代码片段时使用此模式，例如
// 作为出口商品。它假设p.comments中没有待定的注释
// p.comment缓存中最多有一条待处理的注释。
func (p *printer) setComment(g *ast.CommentGroup) {
	if g == nil || !p.useNodeComments {
		return
	}
	if p.comments == nil {
		// 懒洋洋地初始化p.comments
		p.comments = make([]*ast.CommentGroup, 1)
	} else if p.cindex < len(p.comments) {
		// 由于某些原因，存在待定的意见；这
		// 不应该发生-优雅地处理和冲洗
		// 所有评论到g，忽略之后的任何内容
		p.flush(p.posFor(g.List[0].Pos()), token.ILLEGAL)
		p.comments = p.comments[0:1]
		// 在调试模式下，报告错误
		p.internalError("setComment found pending comments")
	}
	p.comments[0] = g
	p.cindex = 0
	// 不要覆盖p.comment缓存中任何挂起的注释
	// （当使用行注释时，可能存在待定注释。）
	// 紧接着是一条没有其他线索的引导评论
	// 之间的代币）
	if p.commentOffset == infinity {
		p.nextComment() // 准备好评论供使用
	}
}

type exprListMode uint

const (
	commaTerm exprListMode = 1 << iota // 列表可以选择以逗号结尾
	noIndent                           // 多行列表中没有额外的缩进
)

// 如果设置了缩进，则多行标识符列表缩进到
// 遇到第一个换行符。
func (p *printer) identList(list []*ast.Ident, indent bool) {
	// 转换为表达式列表，以便我们可以重复使用exprList格式
	xlist := make([]ast.Expr, len(list))
	for i, x := range list {
		xlist[i] = x
	}
	var mode exprListMode
	if !indent {
		mode = noIndent
	}
	p.exprList(token.NoPos, xlist, 1, mode, token.NoPos, false)
}

const filteredMsg = "contains filtered or unexported fields"

// 打印表达式列表。如果列表跨越多个
// 源线之间的原始换行符受到尊重
// 表达。
// None
// toDo（GRI）考虑重写这一点与[AST.ExPR无关]。
// 因此，我们可以对任何类型的列表使用该算法
// （例如，通过一个通道传递列表，在该通道上传递范围）。
func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exprListMode, next0 token.Pos, isIncomplete bool) {
	if len(list) == 0 {
		if isIncomplete {
			prev := p.posFor(prev0)
			next := p.posFor(next0)
			if prev.IsValid() && prev.Line == next.Line {
				p.print("/* " + filteredMsg + " */")
			} else {
				p.print(newline)
				p.print(indent, "// “+FilteredMg，未登录，换行符）
			}
		}
		return
	}

	prev := p.posFor(prev0)
	next := p.posFor(next0)
	line := p.lineFor(list[0].Pos())
	endLine := p.lineFor(list[len(list)-1].End())

	if prev.IsValid() && prev.Line == line && line == endLine {
		// 一行中的所有列表项
		for i, x := range list {
			if i > 0 {
				// 使用逗号后表达式的位置作为
				// 逗号位置用于正确放置注释
				p.print(x.Pos(), token.COMMA, blank)
			}
			p.expr0(x, depth)
		}
		if isIncomplete {
			p.print(token.COMMA, blank, "/* "+filteredMsg+" */")
		}
		return
	}

	// 列表条目跨越多行；
	// 使用源代码位置来引导换行符

	// 如果设置了noIndent，则不要添加额外的缩进；
	// i、 例如，假设第一行已经缩进。
	ws := ignore
	if mode&noIndent == 0 {
		ws = indent
	}

	// 第一个换行符始终是一个formfeed，因为此部分不能
	// 依赖于以前的任何格式。
	prevBreak := -1 // 后跟换行符的最后一个表达式的索引
	if prev.IsValid() && prev.Line < line && p.linebreak(line, 0, ws, true) > 0 {
		ws = ignore
		prevBreak = 0
	}

	// 初始化表达式/键大小：零值表示表达式/键不适合一行
	size := 0

	// 我们使用先前关键尺寸的几何平均值与
	// 用于确定路线中是否应出现中断的当前大小。
	// 为了计算几何平均值，我们累加ln（大小）值（lnsum）
	// 以及包含的大小数（计数）。
	lnsum := 0.0
	count := 0

	// 打印所有列表元素
	prevLine := prev.Line
	for i, x := range list {
		line = p.lineFor(x.Pos())

		// 确定下一个换行符（如果有）是否需要使用formfeed：
		// 通常，使用整个节点大小来做出决策；对于
		// 键：值表达式，使用键大小。
		// TODO（gri）为了获得更好的结果，可能应该将两者结合起来
		// 将密钥和节点大小引入决策过程
		useFF := true

		// 确定元素大小：如果我们没有
		// 上一个和下一个标记的位置信息（可能
		// 生成的代码-在这种情况下，只需通过设置
		// 将其设置为0）。
		prevSize := size
		const infinity = 1e6 // 比任何源线都大
		size = p.nodeSize(x, infinity)
		pair, isPair := x.(*ast.KeyValueExpr)
		if size <= infinity && prev.IsValid() && next.IsValid() {
			// x适合于一条直线
			if isPair {
				size = p.nodeSize(pair.Key, infinity) // 大小<=无穷大
			}
		} else {
			// 尺寸太大或者我们没有好的布局信息
			size = 0
		}

		// 如果前一行和当前行具有单个-
		// 行表达式和键大小较小或
		// 当前关键帧与几何平均值之间的比率
		// 如果以前的密钥大小未超过阈值，
		// 对齐列，不要使用formfeed。
		if prevSize > 0 && size > 0 {
			const smallSize = 40
			if count == 0 || prevSize <= smallSize && size <= smallSize {
				useFF = false
			} else {
				const r = 2.5                               // 门槛
				geomean := math.Exp(lnsum / float64(count)) // 计数>0
				ratio := float64(size) / geomean
				useFF = r*ratio <= 1 || r <= ratio
			}
		}

		needsLinebreak := 0 < prevLine && prevLine < line
		if i > 0 {
			// 使用逗号后表达式的位置作为
			// 逗号位置用于正确的注释位置，但
			// 仅当表达式位于同一行时。
			if !needsLinebreak {
				p.print(x.Pos())
			}
			p.print(token.COMMA)
			needsBlank := true
			if needsLinebreak {
				// 使用换行符断行，以便注释保持对齐
				// 除非设置了useFF或上有多个表达式
				// 使用formfeed的同一行。
				nbreaks := p.linebreak(line, 0, ws, useFF || prevBreak+1 < i)
				if nbreaks > 0 {
					ws = ignore
					prevBreak = i
					needsBlank = false // 我们换行了
				}
				// 如果有一个新区段或多个新行
				// （这意味着tabwriter将隐式中断
				// 部分），重置几何平均变量，因为我们
				// 使用下一个元素启动一组新元素。
				if nbreaks > 1 {
					lnsum = 0
					count = 0
				}
			}
			if needsBlank {
				p.print(blank)
			}
		}

		if len(list) > 1 && isPair && size > 0 && needsLinebreak {
			// 我们有一个键：值表达式，可以放在一行上
			// 它与前面的表达式不在同一行：
			// 使用列作为键，以便连续输入
			// 如果可能，可以对齐。
			// （如果我们之前开始新生产线，则设置needsLinebreak）
			p.expr(pair.Key)
			p.print(pair.Colon, token.COLON, vtab)
			p.expr(pair.Value)
		} else {
			p.expr0(x, depth)
		}

		if size > 0 {
			lnsum += math.Log(float64(size))
			count++
		}

		prevLine = line
	}

	if mode&commaTerm != 0 && next.IsValid() && p.pos.Line < next.Line {
		// 如果下一个标记位于新行上，则打印终止逗号。
		p.print(token.COMMA)
		if isIncomplete {
			p.print(newline)
			p.print("// “+filteredsg）
		}
		if ws == ignore && mode&noIndent == 0 {
			// 如果我们缩进
			p.print(unindent)
		}
		p.print(formfeed) // 终止逗号需要换行才能看起来很好
		return
	}

	if isIncomplete {
		p.print(token.COMMA, newline)
		p.print("// “+filteredsg，换行符）
	}

	if ws == ignore && mode&noIndent == 0 {
		// 如果我们缩进
		p.print(unindent)
	}
}

func (p *printer) parameters(fields *ast.FieldList, isTypeParam bool) {
	openTok, closeTok := token.LPAREN, token.RPAREN
	if isTypeParam {
		openTok, closeTok = token.LBRACK, token.RBRACK
	}
	p.print(fields.Opening, openTok)
	if len(fields.List) > 0 {
		prevLine := p.lineFor(fields.Opening)
		ws := indent
		for i, par := range fields.List {
			// 确定PAR开始和结束线（可能不同）
			// 如果此PAR有多个参数名
			// 或者类型在单独的行上）
			parLineBeg := p.lineFor(par.Pos())
			parLineEnd := p.lineFor(par.End())
			// 如有需要，请分开
			needsLinebreak := 0 < prevLine && prevLine < parLineBeg
			if i > 0 {
				// 使用逗号后面的参数位置作为
				// 逗号位置用于正确放置逗号，但
				// 仅当下一个参数位于同一行时
				if !needsLinebreak {
					p.print(par.Pos())
				}
				p.print(token.COMMA)
			}
			// 分隔符（如果需要）（换行符或空白）
			if needsLinebreak && p.linebreak(parLineBeg, 0, ws, true) > 0 {
				// 如果开口“（”或上一个参数在另一行上结束，则断开该行
				ws = ignore
			} else if i > 0 {
				p.print(blank)
			}
			// 参数名
			if len(par.Names) > 0 {
				// 非常微妙：如果我们在前面缩进（ws==忽略），identList
				// 不会再缩进了。如果我们没有（ws==indent），identList将
				// 如果标识列表跨越多行，则缩进，它将超出缩进
				// 同样在末尾（仍然是ws==indent）。因此，随后的缩进
				// 通过类型后的换行符调用，或在下一个多行标识符列表中
				// 我会做正确的事。
				p.identList(par.Names, ws == indent)
				p.print(blank)
			}
			// 参数类型
			p.expr(stripParensAlways(par.Type))
			prevLine = parLineEnd
		}
		// 如果结束“）”与最后一个参数位于单独的行上，
		// 打印额外的“，”和换行符
		if closing := p.lineFor(fields.Closing); 0 < prevLine && prevLine < closing {
			p.print(token.COMMA)
			p.linebreak(closing, 0, ignore, true)
		}
		// 如果我们缩进
		if ws == ignore {
			p.print(unindent)
		}
	}
	p.print(fields.Closing, closeTok)
}

func (p *printer) signature(sig *ast.FuncType) {
	if tparams := typeparams.Get(sig); tparams != nil {
		p.parameters(tparams, true)
	}
	if sig.Params != nil {
		p.parameters(sig.Params, false)
	} else {
		p.print(token.LPAREN, token.RPAREN)
	}
	res := sig.Results
	n := res.NumFields()
	if n > 0 {
		// res！=无
		p.print(blank)
		if n == 1 && res.List[0].Names == nil {
			// 单一匿名res；否（）'s
			p.expr(stripParensAlways(res.List[0].Type))
			return
		}
		p.parameters(res, false)
	}
}

func identListSize(list []*ast.Ident, maxSize int) (size int) {
	for i, x := range list {
		if i > 0 {
			size += len(", ")
		}
		size += utf8.RuneCountInString(x.Name)
		if size >= maxSize {
			break
		}
	}
	return
}

func (p *printer) isOneLineFieldList(list []*ast.Field) bool {
	if len(list) != 1 {
		return false // 只允许一个字段
	}
	f := list[0]
	if f.Tag != nil || f.Comment != nil {
		return false // 不允许标记或注释
	}
	// 仅名称和类型
	const maxSize = 30 // 根据需要进行调整，这是一个近似值
	namesSize := identListSize(f.Names, maxSize)
	if namesSize > 0 {
		namesSize = 1 // 名称和类型之间为空
	}
	typeSize := p.nodeSize(f.Type, maxSize)
	return namesSize+typeSize <= maxSize
}

func (p *printer) setLineComment(text string) {
	p.setComment(&ast.CommentGroup{List: []*ast.Comment{{Slash: token.NoPos, Text: text}}})
}

func (p *printer) fieldList(fields *ast.FieldList, isStruct, isIncomplete bool) {
	lbrace := fields.Opening
	list := fields.List
	rbrace := fields.Closing
	hasComments := isIncomplete || p.commentBefore(p.posFor(rbrace))
	srcIsOneLine := lbrace.IsValid() && rbrace.IsValid() && p.lineFor(lbrace) == p.lineFor(rbrace)

	if !hasComments && srcIsOneLine {
		// 可能是单行结构/接口
		if len(list) == 0 {
			// 在本例中，关键字和{}之间没有空格
			p.print(lbrace, token.LBRACE, rbrace, token.RBRACE)
			return
		} else if p.isOneLineFieldList(list) {
			// 足够小-一行打印
			// （不要使用identList并忽略源换行符）
			p.print(lbrace, token.LBRACE, blank)
			f := list[0]
			if isStruct {
				for i, x := range f.Names {
					if i > 0 {
						// 没有注释，所以不需要逗号位置
						p.print(token.COMMA, blank)
					}
					p.expr(x)
				}
				if len(f.Names) > 0 {
					p.print(blank)
				}
				p.expr(f.Type)
			} else { // 界面
				if len(f.Names) > 0 {
					// 类型列表类型或方法
					name := f.Names[0] // “类型”或方法名称
					p.expr(name)
					if name.Name == "type" {
						// 类型列表类型
						p.print(blank)
						p.expr(f.Type)
					} else {
						// 方法
						p.signature(f.Type.(*ast.FuncType)) // 不要打印“func”
					}
				} else {
					// 嵌入式接口
					p.expr(f.Type)
				}
			}
			p.print(blank, rbrace, token.RBRACE)
			return
		}
	}
	// 有评论！srcIsOneLine

	p.print(blank, lbrace, token.LBRACE, indent)
	if hasComments || len(list) > 0 {
		p.print(formfeed)
	}

	if isStruct {

		sep := vtab
		if len(list) == 1 {
			sep = blank
		}
		var line int
		for i, f := range list {
			if i > 0 {
				p.linebreak(p.lineFor(f.Pos()), 1, ignore, p.linesFrom(line) > 0)
			}
			extraTabs := 0
			p.setComment(f.Doc)
			p.recordLine(&line)
			if len(f.Names) > 0 {
				// 命名字段
				p.identList(f.Names, false)
				p.print(sep)
				p.expr(f.Type)
				extraTabs = 1
			} else {
				// 匿名字段
				p.expr(f.Type)
				extraTabs = 2
			}
			if f.Tag != nil {
				if len(f.Names) > 0 && sep == vtab {
					p.print(sep)
				}
				p.print(sep)
				p.expr(f.Tag)
				extraTabs = 0
			}
			if f.Comment != nil {
				for ; extraTabs > 0; extraTabs-- {
					p.print(sep)
				}
				p.setComment(f.Comment)
			}
		}
		if isIncomplete {
			if len(list) > 0 {
				p.print(formfeed)
			}
			p.flush(p.posFor(rbrace), token.RBRACE) // 确保我们不会丢失最后一行注释
			p.setLineComment("// “+filteredsg）
		}

	} else { // 界面

		var line int
		var prev *ast.Ident // 前一个“类型”标识符
		for i, f := range list {
			var name *ast.Ident // 名字，还是零
			if len(f.Names) > 0 {
				name = f.Names[0]
			}
			if i > 0 {
				// 如果要打印类型列表，请不要执行换行（min==0）
				// TODO（gri）如果类型列表为
				// 跨越多条线
				min := 1
				if prev != nil && name == prev {
					min = 0
				}
				p.linebreak(p.lineFor(f.Pos()), min, ignore, p.linesFrom(line) > 0)
			}
			p.setComment(f.Doc)
			p.recordLine(&line)
			if name != nil {
				// 类型列表类型或方法
				if name.Name == "type" {
					// 类型列表类型
					if name == prev {
						// 类型是类型列表的一部分
						p.print(token.COMMA, blank)
					} else {
						// 类型启动新的类型列表
						p.print(name, blank)
					}
					p.expr(f.Type)
					prev = name
				} else {
					// 方法
					p.expr(name)
					p.signature(f.Type.(*ast.FuncType)) // 不要打印“func”
					prev = nil
				}
			} else {
				// 嵌入式接口
				p.expr(f.Type)
				prev = nil
			}
			p.setComment(f.Comment)
		}
		if isIncomplete {
			if len(list) > 0 {
				p.print(formfeed)
			}
			p.flush(p.posFor(rbrace), token.RBRACE) // 确保我们不会丢失最后一行注释
			p.setLineComment("// 包含已过滤或未报告的方法“）
		}

	}
	p.print(unindent, formfeed, rbrace, token.RBRACE)
}

// ----------------------------------------------------------------------------
// 表达

func walkBinary(e *ast.BinaryExpr) (has4, has5 bool, maxProblem int) {
	switch e.Op.Precedence() {
	case 4:
		has4 = true
	case 5:
		has5 = true
	}

	switch l := e.X.(type) {
	case *ast.BinaryExpr:
		if l.Op.Precedence() < e.Op.Precedence() {
			// 将插入parens。
			// 假装这是一个*ast.ParenExpr，什么也不做。
			break
		}
		h4, h5, mp := walkBinary(l)
		has4 = has4 || h4
		has5 = has5 || h5
		if maxProblem < mp {
			maxProblem = mp
		}
	}

	switch r := e.Y.(type) {
	case *ast.BinaryExpr:
		if r.Op.Precedence() <= e.Op.Precedence() {
			// 将插入parens。
			// 假装这是一个*ast.ParenExpr，什么也不做。
			break
		}
		h4, h5, mp := walkBinary(r)
		has4 = has4 || h4
		has5 = has5 || h5
		if maxProblem < mp {
			maxProblem = mp
		}

	case *ast.StarExpr:
		if e.Op == token.QUO { // `*/`
			maxProblem = 5
		}

	case *ast.UnaryExpr:
		switch e.Op.String() + r.Op.String() {
		case "/*", "&&", "&^":
			maxProblem = 5
		case "++", "--":
			if maxProblem < 4 {
				maxProblem = 4
			}
		}
	}
	return
}

func cutoff(e *ast.BinaryExpr, depth int) int {
	has4, has5, maxProblem := walkBinary(e)
	if maxProblem > 0 {
		return maxProblem + 1
	}
	if has4 && has5 {
		if depth == 1 {
			return 5
		}
		return 4
	}
	if depth == 1 {
		return 6
	}
	return 4
}

func diffPrec(expr ast.Expr, prec int) int {
	x, ok := expr.(*ast.BinaryExpr)
	if !ok || prec != x.Op.Precedence() {
		return 1
	}
	return 0
}

func reduceDepth(depth int) int {
	depth--
	if depth < 1 {
		depth = 1
	}
	return depth
}

// 格式化二进制表达式：确定截止值，然后格式化。
// 让我们调用depth==1普通模式，depth>1紧凑模式。
// （Russ Cox提出的算法建议。）
// None
// 这些先例是：
// 5             *  /  %  <<  >>  &  &^
// 4             +  -  |  ^
// 3             ==  !=  <  <=  >  >=
// 2             &&
// 1             ||
// None
// 唯一的决定是在第4层和第5层周围是否有空间。
// 级别6（一元数）上从来没有空格，级别3及以下始终有空格。
// None
// 要选择截止点，请查看整个表达式，但不包括主表达式
// 表达式（函数调用、括号中的表达式），并应用以下规则：
// None
// 1） 如果存在具有右侧一元操作数的二元运算符
// 如果没有空格会发生冲突，则必须（按顺序）切断：
// None
// /*	6
// &&	6
// &^	6
// ++	5
// --	5
// None
// （比较运算符的周围总是有空格。）
// None
// 2） 如果混合了5级和4级操作员，则切断
// 正常模式下为5（使用空格区分优先级）
// 紧凑模式下的4（从不使用空格）。
// None
// 3） 如果没有4级操作员或5级操作员，则
// 正常模式下的截止值为6（始终使用空格）
// 紧凑模式下的4（从不使用空格）。
// None
func (p *printer) binaryExpr(x *ast.BinaryExpr, prec1, cutoff, depth int) {
	prec := x.Op.Precedence()
	if prec < prec1 {
		// 需要括号
		// 注意：解析器插入一个ast.ParenExpr节点；所以这个案子
		// 只有以不同的方式创建AST时才能发生。
		p.print(token.LPAREN)
		p.expr0(x, reduceDepth(depth)) // 括号撤消一级深度
		p.print(token.RPAREN)
		return
	}

	printBlank := prec < cutoff

	ws := indent
	p.expr1(x.X, prec, depth+diffPrec(x.X, prec))
	if printBlank {
		p.print(blank)
	}
	xline := p.pos.Line // 在操作员之前（可能在下一行！）
	yline := p.lineFor(x.Y.Pos())
	p.print(x.OpPos, x.Op)
	if xline != yline && xline > 0 && yline > 0 {
		// 至少有一个换行符，但要考虑额外的空行
		// 在源头
		if p.linebreak(yline, 1, ws, true) > 0 {
			ws = ignore
			printBlank = false // 换行后无空白
		}
	}
	if printBlank {
		p.print(blank)
	}
	p.expr1(x.Y, prec+1, depth+1)
	if ws == ignore {
		p.print(unindent)
	}
}

func isBinary(expr ast.Expr) bool {
	_, ok := expr.(*ast.BinaryExpr)
	return ok
}

func (p *printer) expr1(expr ast.Expr, prec1, depth int) {
	p.print(expr.Pos())

	switch x := expr.(type) {
	case *ast.BadExpr:
		p.print("BadExpr")

	case *ast.Ident:
		p.print(x)

	case *ast.BinaryExpr:
		if depth < 1 {
			p.internalError("depth < 1:", depth)
			depth = 1
		}
		p.binaryExpr(x, prec1, cutoff(x, depth), depth)

	case *ast.KeyValueExpr:
		p.expr(x.Key)
		p.print(x.Colon, token.COLON, blank)
		p.expr(x.Value)

	case *ast.StarExpr:
		const prec = token.UnaryPrec
		if prec < prec1 {
			// 需要括号
			p.print(token.LPAREN)
			p.print(token.MUL)
			p.expr(x.X)
			p.print(token.RPAREN)
		} else {
			// 不需要括号
			p.print(token.MUL)
			p.expr(x.X)
		}

	case *ast.UnaryExpr:
		const prec = token.UnaryPrec
		if prec < prec1 {
			// 需要括号
			p.print(token.LPAREN)
			p.expr(x)
			p.print(token.RPAREN)
		} else {
			// 不需要括号
			p.print(x.Op)
			if x.Op == token.RANGE {
				// TODO（gri）如果无法访问此代码，请将其删除。
				p.print(blank)
			}
			p.expr1(x.X, prec, depth)
		}

	case *ast.BasicLit:
		if p.Config.Mode&normalizeNumbers != 0 {
			x = normalizedNumber(x)
		}
		p.print(x)

	case *ast.FuncLit:
		p.print(x.Type.Pos(), token.FUNC)
		// 请参阅funcDecl中有关如何计算标头大小的注释。
		startCol := p.out.Column - len("func")
		p.signature(x.Type)
		p.funcBody(p.distanceFrom(x.Type.Pos(), startCol), blank, x.Body)

	case *ast.ParenExpr:
		if _, hasParens := x.X.(*ast.ParenExpr); hasParens {
			// 不要在已插入括号的表达式周围打印括号
			// toDo（GRI）考虑使这更通用，并纳入优先级别。
			p.expr0(x.X, depth)
		} else {
			p.print(token.LPAREN)
			p.expr0(x.X, reduceDepth(depth)) // 括号撤消一级深度
			p.print(x.Rparen, token.RPAREN)
		}

	case *ast.SelectorExpr:
		p.selectorExpr(x, depth, false)

	case *ast.TypeAssertExpr:
		p.expr1(x.X, token.HighestPrec, depth)
		p.print(token.PERIOD, x.Lparen, token.LPAREN)
		if x.Type != nil {
			p.expr(x.Type)
		} else {
			p.print(token.TYPE)
		}
		p.print(x.Rparen, token.RPAREN)

	case *ast.IndexExpr:
		// TODO（gri）：应将[]视为括号，并撤消一级深度
		p.expr1(x.X, token.HighestPrec, 1)
		p.print(x.Lbrack, token.LBRACK)
		// 注意：我们在这里有点防御性，以处理的ListXPR的情况
		// 长度1。
		if list := typeparams.UnpackExpr(x.Index); len(list) > 0 {
			if len(list) > 1 {
				p.exprList(x.Lbrack, list, depth+1, commaTerm, x.Rbrack, false)
			} else {
				p.expr0(list[0], depth+1)
			}
		} else {
			p.expr0(x.Index, depth+1)
		}
		p.print(x.Rbrack, token.RBRACK)

	case *ast.SliceExpr:
		// TODO（gri）：应将[]视为括号，并撤消一级深度
		p.expr1(x.X, token.HighestPrec, 1)
		p.print(x.Lbrack, token.LBRACK)
		indices := []ast.Expr{x.Low, x.High}
		if x.Max != nil {
			indices = append(indices, x.Max)
		}
		// 确定是否需要在“：”周围添加空格
		var needsBlanks bool
		if depth <= 1 {
			var indexCount int
			var hasBinaries bool
			for _, x := range indices {
				if x != nil {
					indexCount++
					if isBinary(x) {
						hasBinaries = true
					}
				}
			}
			if indexCount > 1 && hasBinaries {
				needsBlanks = true
			}
		}
		for i, x := range indices {
			if i > 0 {
				if indices[i-1] != nil && needsBlanks {
					p.print(blank)
				}
				p.print(token.COLON)
				if x != nil && needsBlanks {
					p.print(blank)
				}
			}
			if x != nil {
				p.expr0(x, depth+1)
			}
		}
		p.print(x.Rbrack, token.RBRACK)

	case *ast.CallExpr:
		if len(x.Args) > 1 {
			depth++
		}
		var wasIndented bool
		if _, ok := x.Fun.(*ast.FuncType); ok {
			// 转换为文字函数类型需要在类型周围加括号
			p.print(token.LPAREN)
			wasIndented = p.possibleSelectorExpr(x.Fun, token.HighestPrec, depth)
			p.print(token.RPAREN)
		} else {
			wasIndented = p.possibleSelectorExpr(x.Fun, token.HighestPrec, depth)
		}
		p.print(x.Lparen, token.LPAREN)
		if x.Ellipsis.IsValid() {
			p.exprList(x.Lparen, x.Args, depth, 0, x.Ellipsis, false)
			p.print(x.Ellipsis, token.ELLIPSIS)
			if x.Rparen.IsValid() && p.lineFor(x.Ellipsis) < p.lineFor(x.Rparen) {
				p.print(token.COMMA, formfeed)
			}
		} else {
			p.exprList(x.Lparen, x.Args, depth, commaTerm, x.Rparen, false)
		}
		p.print(x.Rparen, token.RPAREN)
		if wasIndented {
			p.print(unindent)
		}

	case *ast.CompositeLit:
		// 作为复合文字本身的复合文字元素可能会忽略该类型
		if x.Type != nil {
			p.expr1(x.Type, token.HighestPrec, depth)
		}
		p.level++
		p.print(x.Lbrace, token.LBRACE)
		p.exprList(x.Lbrace, x.Elts, 1, commaTerm, x.Rbrace, x.Incomplete)
		// 不要在/*式注释后插入额外的换行符
		// 在关闭“}”之前，因为如果存在
		// 没有尾随'，'
		mode := noExtraLinebreak
		// 请勿在/*-样式的注释后插入额外的空白
		// 在结束“}”之前，除非文本为空
		if len(x.Elts) > 0 {
			mode |= noExtraBlank
		}
		// 需要初始缩进以打印单独的注释
		// 适当的压痕水平
		p.print(indent, unindent, mode, x.Rbrace, token.RBRACE, mode)
		p.level--

	case *ast.Ellipsis:
		p.print(token.ELLIPSIS)
		if x.Elt != nil {
			p.expr(x.Elt)
		}

	case *ast.ArrayType:
		p.print(token.LBRACK)
		if x.Len != nil {
			p.expr(x.Len)
		}
		p.print(token.RBRACK)
		p.expr(x.Elt)

	case *ast.StructType:
		p.print(token.STRUCT)
		p.fieldList(x.Fields, true, x.Incomplete)

	case *ast.FuncType:
		p.print(token.FUNC)
		p.signature(x)

	case *ast.InterfaceType:
		p.print(token.INTERFACE)
		p.fieldList(x.Methods, false, x.Incomplete)

	case *ast.MapType:
		p.print(token.MAP, token.LBRACK)
		p.expr(x.Key)
		p.print(token.RBRACK)
		p.expr(x.Value)

	case *ast.ChanType:
		switch x.Dir {
		case ast.SEND | ast.RECV:
			p.print(token.CHAN)
		case ast.RECV:
			p.print(token.ARROW, token.CHAN) // x、 箭头和x.Pos（）是相同的
		case ast.SEND:
			p.print(token.CHAN, x.Arrow, token.ARROW)
		}
		p.print(blank)
		p.expr(x.Value)

	default:
		panic("unreachable")
	}
}

// normalizedNumber重写基本前缀和指数
// 使用小写字母（0X123到0X123和1.2E3到1.2E3）的数字的数量，
// 并从整数假想文字（0765i到765i）中删除前导0。
// 它只保留十六进制数字。
// None
// normalizedNumber不会将点亮的ast.BasicLit值修改为。
// 如果lit不是一个数字或已不是标准格式的数字，
// lit按原样返回。否则将创建一个新的ast.BasicLit。
func normalizedNumber(lit *ast.BasicLit) *ast.BasicLit {
	if lit.Kind != token.INT && lit.Kind != token.FLOAT && lit.Kind != token.IMAG {
		return lit // 不是数字-无事可做
	}
	if len(lit.Value) < 2 {
		return lit // 只有一位数字（常见情况）-无需操作
	}
	// len（发光值）>=2

	// 我们忽略lit.Kind，因为对于lit.Kind==token.IMAG，文本可能是整数
	// 或浮点值，十进制或非十进制。相反，只考虑文字模式。
	x := lit.Value
	switch x[:2] {
	default:
		// 0-前缀八进制、十进制整数或浮点（可能带有'i'后缀）
		if i := strings.LastIndexByte(x, 'E'); i >= 0 {
			x = x[:i] + "e" + x[i+1:]
			break
		}
		// 从整数（但不是浮点）虚文字中删除前导0
		if x[len(x)-1] == 'i' && strings.IndexByte(x, '.') < 0 && strings.IndexByte(x, 'e') < 0 {
			x = strings.TrimLeft(x, "0_")
			if x == "i" {
				x = "0i"
			}
		}
	case "0X":
		x = "0x" + x[2:]
		// 可能是十六进制浮点
		if i := strings.LastIndexByte(x, 'P'); i >= 0 {
			x = x[:i] + "p" + x[i+1:]
		}
	case "0x":
		// 可能是十六进制浮点
		i := strings.LastIndexByte(x, 'P')
		if i == -1 {
			return lit // 无事可做
		}
		x = x[:i] + "p" + x[i+1:]
	case "0O":
		x = "0o" + x[2:]
	case "0o":
		return lit // 无事可做
	case "0B":
		x = "0b" + x[2:]
	case "0b":
		return lit // 无事可做
	}

	return &ast.BasicLit{ValuePos: lit.ValuePos, Kind: lit.Kind, Value: x}
}

func (p *printer) possibleSelectorExpr(expr ast.Expr, prec1, depth int) bool {
	if x, ok := expr.(*ast.SelectorExpr); ok {
		return p.selectorExpr(x, depth, true)
	}
	p.expr1(expr, prec1, depth)
	return false
}

// selectorExpr处理*ast.selectorExpr节点并报告x是否跨越
// 多行。
func (p *printer) selectorExpr(x *ast.SelectorExpr, depth int, isMethod bool) bool {
	p.expr1(x.X, token.HighestPrec, depth)
	p.print(token.PERIOD)
	if line := p.lineFor(x.Sel.Pos()); p.pos.IsValid() && p.pos.Line < line {
		p.print(indent, newline, x.Sel.Pos(), x.Sel)
		if !isMethod {
			p.print(unindent)
		}
		return true
	}
	p.print(x.Sel.Pos(), x.Sel)
	return false
}

func (p *printer) expr0(x ast.Expr, depth int) {
	p.expr1(x, token.LowestPrec, depth)
}

func (p *printer) expr(x ast.Expr) {
	const depth = 1
	p.expr1(x, token.LowestPrec, depth)
}

// ----------------------------------------------------------------------------
// 声明

// 打印缩进的语句列表，但在最后一条语句后没有换行符。
// 源代码中语句之间的额外换行符受尊重，但最多为一行
// 语句之间打印空行。
func (p *printer) stmtList(list []ast.Stmt, nindent int, nextIsRBrace bool) {
	if nindent > 0 {
		p.print(indent)
	}
	var line int
	i := 0
	for _, s := range list {
		// 忽略空语句（was问题3466）
		if _, isEmpty := s.(*ast.EmptyStmt); !isEmpty {
			// nindent==0，仅适用于switch/select case子句列表；
			// 在这种情况下，每一条款都是一个新的章节
			if len(p.output) > 0 {
				// 只有在不在输出开始时才打印换行符
				// （即，我们不仅打印部分程序）
				p.linebreak(p.lineFor(s.Pos()), 1, ignore, i == 0 || nindent == 0 || p.linesFrom(line) > 0)
			}
			p.recordLine(&line)
			p.stmt(s, nextIsRBrace && i == len(list)-1)
			// 标签语句将标签放在单独的一行上，但在这里
			// 我们只关心实际语句的起始行
			// 无标签-每个标签的正确行
			for t := s; ; {
				lt, _ := t.(*ast.LabeledStmt)
				if lt == nil {
					break
				}
				line++
				t = lt.Stmt
			}
			i++
		}
	}
	if nindent > 0 {
		p.print(unindent)
	}
}

// 块打印*ast.BlockStmt；它总是至少跨越两条线。
func (p *printer) block(b *ast.BlockStmt, nindent int) {
	p.print(b.Lbrace, token.LBRACE)
	p.stmtList(b.List, nindent, true)
	p.linebreak(p.lineFor(b.Rbrace), 1, ignore, true)
	p.print(b.Rbrace, token.RBRACE)
}

func isTypeName(x ast.Expr) bool {
	switch t := x.(type) {
	case *ast.Ident:
		return true
	case *ast.SelectorExpr:
		return isTypeName(t.X)
	}
	return false
}

func stripParens(x ast.Expr) ast.Expr {
	if px, strip := x.(*ast.ParenExpr); strip {
		// 如果有括号，则不得删除括号
		// 以
		// 类型名
		ast.Inspect(px.X, func(node ast.Node) bool {
			switch x := node.(type) {
			case *ast.ParenExpr:
				// 括号保护封闭的复合文字
				return false
			case *ast.CompositeLit:
				if isTypeName(x.Type) {
					strip = false // 不要去掉括号
				}
				return false
			}
			// 在所有其他情况下，继续检查
			return true
		})
		if strip {
			return stripParens(px.X)
		}
	}
	return x
}

func stripParensAlways(x ast.Expr) ast.Expr {
	if x, ok := x.(*ast.ParenExpr); ok {
		return stripParensAlways(x.X)
	}
	return x
}

func (p *printer) controlClause(isForStmt bool, init ast.Stmt, expr ast.Expr, post ast.Stmt) {
	p.print(blank)
	needsBlank := false
	if init == nil && post == nil {
		// 不需要分号
		if expr != nil {
			p.expr(stripParens(expr))
			needsBlank = true
		}
	} else {
		// 需要所有分号
		// （它们不是分隔符，请明确打印）
		if init != nil {
			p.stmt(init, false)
		}
		p.print(token.SEMICOLON, blank)
		if expr != nil {
			p.expr(stripParens(expr))
			needsBlank = true
		}
		if isForStmt {
			p.print(token.SEMICOLON, blank)
			needsBlank = false
			if post != nil {
				p.stmt(post, false)
				needsBlank = true
			}
		}
	}
	if needsBlank {
		p.print(blank)
	}
}

// indentList报告表达式列表如果
// 全部缩进（从第一个元素开始，而不是
// 而不是从第一个换行符开始）。
// None
func (p *printer) indentList(list []ast.Expr) bool {
	// 启发式：indentList报告是否有多个多个-
	// 列表中的行元素，或者如果有任何元素不是
	// 从与前一行相同的行开始，结束。
	if len(list) >= 2 {
		var b = p.lineFor(list[0].Pos())
		var e = p.lineFor(list[len(list)-1].End())
		if 0 < b && b < e {
			// 列表跨越多行
			n := 0 // 多行元素计数
			line := b
			for _, x := range list {
				xb := p.lineFor(x.Pos())
				xe := p.lineFor(x.End())
				if line < xb {
					// x不是从同一个位置开始的
					// 与前一行一样结束
					return true
				}
				if xb < xe {
					// x是一个多行元素
					n++
				}
				line = xe
			}
			return n > 1
		}
	}
	return false
}

func (p *printer) stmt(stmt ast.Stmt, nextIsRBrace bool) {
	p.print(stmt.Pos())

	switch s := stmt.(type) {
	case *ast.BadStmt:
		p.print("BadStmt")

	case *ast.DeclStmt:
		p.decl(s.Decl)

	case *ast.EmptyStmt:
		// 无事可做

	case *ast.LabeledStmt:
		// 断线后立即“纠正”不凹痕
		// 如果没有注释，则在换行符之前应用
		// 介于之间（请参见writeWhitespace）
		p.print(unindent)
		p.expr(s.Label)
		p.print(s.Colon, token.COLON, indent)
		if e, isEmpty := s.Stmt.(*ast.EmptyStmt); isEmpty {
			if !nextIsRBrace {
				p.print(newline, e.Pos(), token.SEMICOLON)
				break
			}
		} else {
			p.linebreak(p.lineFor(s.Stmt.Pos()), 1, ignore, true)
		}
		p.stmt(s.Stmt, nextIsRBrace)

	case *ast.ExprStmt:
		const depth = 1
		p.expr0(s.X, depth)

	case *ast.SendStmt:
		const depth = 1
		p.expr0(s.Chan, depth)
		p.print(blank, s.Arrow, token.ARROW, blank)
		p.expr0(s.Value, depth)

	case *ast.IncDecStmt:
		const depth = 1
		p.expr0(s.X, depth+1)
		p.print(s.TokPos, s.Tok)

	case *ast.AssignStmt:
		var depth = 1
		if len(s.Lhs) > 1 && len(s.Rhs) > 1 {
			depth++
		}
		p.exprList(s.Pos(), s.Lhs, depth, 0, s.TokPos, false)
		p.print(blank, s.TokPos, s.Tok, blank)
		p.exprList(s.TokPos, s.Rhs, depth, 0, token.NoPos, false)

	case *ast.GoStmt:
		p.print(token.GO, blank)
		p.expr(s.Call)

	case *ast.DeferStmt:
		p.print(token.DEFER, blank)
		p.expr(s.Call)

	case *ast.ReturnStmt:
		p.print(token.RETURN)
		if s.Results != nil {
			p.print(blank)
			// 使用缩进列表启发式使角案例看起来
			// 更好（第1207期）。一种更系统的方法将是可行的
			// 始终缩进，但这将导致严重的
			// 重新格式化代码库，但不一定
			// 一般来说，这会导致生成格式更好的代码。
			if p.indentList(s.Results) {
				p.print(indent)
				// 使用NoPos，这样新行就永远不会出现
				// 结果（见第32854期）。
				p.exprList(token.NoPos, s.Results, 1, noIndent, token.NoPos, false)
				p.print(unindent)
			} else {
				p.exprList(token.NoPos, s.Results, 1, 0, token.NoPos, false)
			}
		}

	case *ast.BranchStmt:
		p.print(s.Tok)
		if s.Label != nil {
			p.print(blank)
			p.expr(s.Label)
		}

	case *ast.BlockStmt:
		p.block(s, 1)

	case *ast.IfStmt:
		p.print(token.IF)
		p.controlClause(false, s.Init, s.Cond, nil)
		p.block(s.Body, 1)
		if s.Else != nil {
			p.print(blank, token.ELSE, blank)
			switch s.Else.(type) {
			case *ast.BlockStmt, *ast.IfStmt:
				p.stmt(s.Else, nextIsRBrace)
			default:
				// 这只能在错误的情况下发生
				// 构建AST。允许，但要打印出来
				// 它可以被无错误地解析。
				p.print(token.LBRACE, indent, formfeed)
				p.stmt(s.Else, true)
				p.print(unindent, formfeed, token.RBRACE)
			}
		}

	case *ast.CaseClause:
		if s.List != nil {
			p.print(token.CASE, blank)
			p.exprList(s.Pos(), s.List, 1, 0, s.Colon, false)
		} else {
			p.print(token.DEFAULT)
		}
		p.print(s.Colon, token.COLON)
		p.stmtList(s.Body, 1, nextIsRBrace)

	case *ast.SwitchStmt:
		p.print(token.SWITCH)
		p.controlClause(false, s.Init, s.Tag, nil)
		p.block(s.Body, 0)

	case *ast.TypeSwitchStmt:
		p.print(token.SWITCH)
		if s.Init != nil {
			p.print(blank)
			p.stmt(s.Init, false)
			p.print(token.SEMICOLON)
		}
		p.print(blank)
		p.stmt(s.Assign, false)
		p.print(blank)
		p.block(s.Body, 0)

	case *ast.CommClause:
		if s.Comm != nil {
			p.print(token.CASE, blank)
			p.stmt(s.Comm, false)
		} else {
			p.print(token.DEFAULT)
		}
		p.print(s.Colon, token.COLON)
		p.stmtList(s.Body, 1, nextIsRBrace)

	case *ast.SelectStmt:
		p.print(token.SELECT, blank)
		body := s.Body
		if len(body.List) == 0 && !p.commentBefore(p.posFor(body.Rbrace)) {
			// 在一行上打印不带注释的空select语句
			p.print(body.Lbrace, token.LBRACE, body.Rbrace, token.RBRACE)
		} else {
			p.block(body, 0)
		}

	case *ast.ForStmt:
		p.print(token.FOR)
		p.controlClause(true, s.Init, s.Cond, s.Post)
		p.block(s.Body, 1)

	case *ast.RangeStmt:
		p.print(token.FOR, blank)
		if s.Key != nil {
			p.expr(s.Key)
			if s.Value != nil {
				// 将逗号后面的值位置用作
				// 逗号位置用于正确放置注释
				p.print(s.Value.Pos(), token.COMMA, blank)
				p.expr(s.Value)
			}
			p.print(blank, s.TokPos, s.Tok, blank)
		}
		p.print(token.RANGE, blank)
		p.expr(stripParens(s.X))
		p.print(blank)
		p.block(s.Body, 1)

	default:
		panic("unreachable")
	}
}

// ----------------------------------------------------------------------------
// 声明

// keepTypeColumn函数确定
// 必须保留连续的const或var声明，或者如果初始化
// 值（V）可以放在类型列（T）中。第i个条目
// 在结果中，如果必须保留spec[i]中的type列，则slice为true。
// None
// 例如，宣言：
// None
// 常数(
// foobar int=42
// x=7
// 福
// 巴=991
// )
// None
// 引出下面的类型/值矩阵。运行值列（V）可以
// 如果没有任何值的类型，则将其移动到“类型”列中
// 在该列中（我们只移动整个列以便它们正确对齐）。
// None
// 矩阵格式结果
// 矩阵
// T V->T V->true存在一个T，因此类型
// -必须保留V-V true列
// -----错
// -V-假V移到T列中
// None
func keepTypeColumn(specs []ast.Spec) []bool {
	m := make([]bool, len(specs))

	populate := func(i, j int, keepType bool) {
		if keepType {
			for ; i < j; i++ {
				m[i] = true
			}
		}
	}

	i0 := -1 // 如果i0>=0，我们正在运行，i0是运行的开始
	var keepType bool
	for i, s := range specs {
		t := s.(*ast.ValueSpec)
		if t.Values != nil {
			if i0 < 0 {
				// 开始使用非零值运行ValueSpec
				i0 = i
				keepType = false
			}
		} else {
			if i0 >= 0 {
				// 跑完
				populate(i0, i, keepType)
				i0 = -1
			}
		}
		if t.Type != nil {
			keepType = true
		}
	}
	if i0 >= 0 {
		// 跑完
		populate(i0, len(specs), keepType)
	}

	return m
}

func (p *printer) valueSpec(s *ast.ValueSpec, keepType bool) {
	p.setComment(s.Doc)
	p.identList(s.Names, false) // 始终存在
	extraTabs := 3
	if s.Type != nil || keepType {
		p.print(vtab)
		extraTabs--
	}
	if s.Type != nil {
		p.expr(s.Type)
	}
	if s.Values != nil {
		p.print(vtab, token.ASSIGN, blank)
		p.exprList(token.NoPos, s.Values, 1, 0, token.NoPos, false)
		extraTabs--
	}
	if s.Comment != nil {
		for ; extraTabs > 0; extraTabs-- {
			p.print(vtab)
		}
		p.setComment(s.Comment)
	}
}

func sanitizeImportPath(lit *ast.BasicLit) *ast.BasicLit {
	// 注意：go/解析器生成的未修改的AST已经
	// 包含向后或双引号路径字符串，该字符串
	// 不包含任何无效字符，并且大部分工作
	// 这是不需要的。但是，修改或生成的AST
	// 可能包含非规范路径。在办公室里做这项工作
	// 所有情况下，因为它不是太难，也不是速度关键。

	// 如果我们没有一个合适的字符串，保守一点，返回我们所有的字符串
	if lit.Kind != token.STRING {
		return lit
	}
	s, err := strconv.Unquote(lit.Value)
	if err != nil {
		return lit
	}

	// 如果字符串是无效的路径，则返回我们拥有的任何内容
	// None
	// 规范：“实现限制：编译器可能会限制
	// 仅使用属于的字符将路径导入非空字符串
	// 到Unicode的L、M、N、P和s通用类别（图形
	// 不带空格的字符），也可以排除这些字符
	// !“#$%&'（）*，：；<=>？[\]^`{124;}和Unicode替换字符
	// U+FFFD。”
	if s == "" {
		return lit
	}
	const illegalChars = `!"#$%&'()*,:;<=>?[\]^{|}` + "`\uFFFD"
	for _, r := range s {
		if !unicode.IsGraphic(r) || unicode.IsSpace(r) || strings.ContainsRune(illegalChars, r) {
			return lit
		}
	}

	// 否则，返回双引号路径
	s = strconv.Quote(s)
	if s == lit.Value {
		return lit // 灯没问题
	}
	return &ast.BasicLit{ValuePos: lit.ValuePos, Kind: token.STRING, Value: s}
}

// 参数n是组中的规格数。如果设置了doIndent，
// 规范中的多行标识符列表在第一个
// 遇到换行符。
// None
func (p *printer) spec(spec ast.Spec, n int, doIndent bool) {
	switch s := spec.(type) {
	case *ast.ImportSpec:
		p.setComment(s.Doc)
		if s.Name != nil {
			p.expr(s.Name)
			p.print(blank)
		}
		p.expr(sanitizeImportPath(s.Path))
		p.setComment(s.Comment)
		p.print(s.EndPos)

	case *ast.ValueSpec:
		if n != 1 {
			p.internalError("expected n = 1; got", n)
		}
		p.setComment(s.Doc)
		p.identList(s.Names, doIndent) // 始终存在
		if s.Type != nil {
			p.print(blank)
			p.expr(s.Type)
		}
		if s.Values != nil {
			p.print(blank, token.ASSIGN, blank)
			p.exprList(token.NoPos, s.Values, 1, 0, token.NoPos, false)
		}
		p.setComment(s.Comment)

	case *ast.TypeSpec:
		p.setComment(s.Doc)
		p.expr(s.Name)
		if tparams := typeparams.Get(s); tparams != nil {
			p.parameters(tparams, true)
		}
		if n == 1 {
			p.print(blank)
		} else {
			p.print(vtab)
		}
		if s.Assign.IsValid() {
			p.print(token.ASSIGN, blank)
		}
		p.expr(s.Type)
		p.setComment(s.Comment)

	default:
		panic("unreachable")
	}
}

func (p *printer) genDecl(d *ast.GenDecl) {
	p.setComment(d.Doc)
	p.print(d.Pos(), d.Tok, blank)

	if d.Lparen.IsValid() || len(d.Specs) > 1 {
		// 括号内声明组
		p.print(d.Lparen, token.LPAREN)
		if n := len(d.Specs); n > 0 {
			p.print(indent, formfeed)
			if n > 1 && (d.Tok == token.CONST || d.Tok == token.VAR) {
				// 两个或多个分组常量/变量声明：
				// 确定是否必须保留类型列
				keepType := keepTypeColumn(d.Specs)
				var line int
				for i, s := range d.Specs {
					if i > 0 {
						p.linebreak(p.lineFor(s.Pos()), 1, ignore, p.linesFrom(line) > 0)
					}
					p.recordLine(&line)
					p.valueSpec(s.(*ast.ValueSpec), keepType[i])
				}
			} else {
				var line int
				for i, s := range d.Specs {
					if i > 0 {
						p.linebreak(p.lineFor(s.Pos()), 1, ignore, p.linesFrom(line) > 0)
					}
					p.recordLine(&line)
					p.spec(s, n, false)
				}
			}
			p.print(unindent, formfeed)
		}
		p.print(d.Rparen, token.RPAREN)

	} else if len(d.Specs) > 0 {
		// 单一声明
		p.spec(d.Specs[0], 1, true)
	}
}

// nodeSize确定格式化后n的大小（以字符为单位）。
// 如果节点在一条具有at的直线上拟合，则结果为<=maxSize
// 大多数maxSize字符和格式化输出不包含
// 任何控制字符。否则，结果为>maxSize。
// None
func (p *printer) nodeSize(n ast.Node, maxSize int) (size int) {
	// nodeSize调用打印机，打印机可能会调用nodeSize
	// 递归地。对于深层复合文字嵌套，可以
	// 导出一个指数算法。还记得以前吗
	// 修剪递归的结果（WAS1628版）。
	if size, found := p.nodeSizes[n]; found {
		return size
	}

	size = maxSize + 1 // 假设n不合适
	p.nodeSizes[n] = size

	// 节点大小计算必须独立于特定的
	// 风格使我们总是得到相同的决定；打印
	// 原始格式
	cfg := Config{Mode: RawFormat}
	var buf bytes.Buffer
	if err := cfg.fprint(&buf, p.fset, n, p.nodeSizes); err != nil {
		return
	}
	if buf.Len() <= maxSize {
		for _, ch := range buf.Bytes() {
			if ch < ' ' {
				return
			}
		}
		size = buf.Len() // n适合
		p.nodeSizes[n] = size
	}
	return
}

// numLines返回原始源中节点n跨越的行数。
func (p *printer) numLines(n ast.Node) int {
	if from := n.Pos(); from.IsValid() {
		if to := n.End(); to.IsValid() {
			return p.lineFor(to) - p.lineFor(from) + 1
		}
	}
	return infinity
}

// bodySize类似于nodeSize，但它专门用于*ast.BlockStmt。
func (p *printer) bodySize(b *ast.BlockStmt, maxSize int) int {
	pos1 := b.Pos()
	pos2 := b.Rbrace
	if pos1.IsValid() && pos2.IsValid() && p.lineFor(pos1) != p.lineFor(pos2) {
		// 打开和关闭支架位于不同的线路上-不要使其成为一条线路
		return maxSize + 1
	}
	if len(b.List) > 5 {
		// 太多的陈述——不要只说一句话
		return maxSize + 1
	}
	// 否则，估计身体大小
	bodySize := p.commentSizeBefore(p.posFor(pos2))
	for i, s := range b.List {
		if bodySize > maxSize {
			break // 没有必要继续
		}
		if i > 0 {
			bodySize += 2 // 分号的空格和空格
		}
		bodySize += p.nodeSize(s, maxSize)
	}
	return bodySize
}

// funcBody在给定headerSize的函数头后面打印函数体。
// 如果标题和块的大小“足够小”且块“足够简单”，
// 块打印在当前行上，没有换行，与页眉隔开
// 到9月，否则块的开头“{”将打印在当前行上，后面是
// 块语句的行及其结束“}”。
// None
func (p *printer) funcBody(headerSize int, sep whiteSpace, b *ast.BlockStmt) {
	if b == nil {
		return
	}

	// 保存/恢复复合文字嵌套级别
	defer func(level int) {
		p.level = level
	}(p.level)
	p.level = 0

	const maxSize = 100
	if headerSize+p.bodySize(b, maxSize) <= maxSize {
		p.print(sep, b.Lbrace, token.LBRACE)
		if len(b.List) > 0 {
			p.print(blank)
			for i, s := range b.List {
				if i > 0 {
					p.print(token.SEMICOLON, blank)
				}
				p.stmt(s, i == len(b.List)-1)
			}
			p.print(blank)
		}
		p.print(noExtraLinebreak, b.Rbrace, token.RBRACE, noExtraLinebreak)
		return
	}

	if sep != ignore {
		p.print(blank) // 始终使用空白
	}
	p.block(b, 1)
}

// distanceFrom返回p.out（当前输出）之间的列差
// 位置）和startOutCol。如果起始位置位于与之不同的线上
// 当前位置（或其中一个未知），结果为无穷大。
func (p *printer) distanceFrom(startPos token.Pos, startOutCol int) int {
	if startPos.IsValid() && p.pos.IsValid() && p.posFor(startPos).Line == p.pos.Line {
		return p.out.Column - startOutCol
	}
	return infinity
}

func (p *printer) funcDecl(d *ast.FuncDecl) {
	p.setComment(d.Doc)
	p.print(d.Pos(), token.FUNC, blank)
	// 只有在发出FUNC后，我们才能保存startCol；否则，它可能会在
	// 不同的行（仅当
	// FUNC已发出）。
	startCol := p.out.Column - len("func ")
	if d.Recv != nil {
		p.parameters(d.Recv, false) // 方法：打印接收器
		p.print(blank)
	}
	p.expr(d.Name)
	p.signature(d.Type)
	p.funcBody(p.distanceFrom(d.Pos(), startCol), vtab, d.Body)
}

func (p *printer) decl(decl ast.Decl) {
	switch d := decl.(type) {
	case *ast.BadDecl:
		p.print(d.Pos(), "BadDecl")
	case *ast.GenDecl:
		p.genDecl(d)
	case *ast.FuncDecl:
		p.funcDecl(d)
	default:
		panic("unreachable")
	}
}

// ----------------------------------------------------------------------------
// 文件夹

func declToken(decl ast.Decl) (tok token.Token) {
	tok = token.ILLEGAL
	switch d := decl.(type) {
	case *ast.GenDecl:
		tok = d.Tok
	case *ast.FuncDecl:
		tok = token.FUNC
	}
	return
}

func (p *printer) declList(list []ast.Decl) {
	tok := token.ILLEGAL
	for _, d := range list {
		prev := tok
		tok = declToken(d)
		// 如果声明标记已更改（例如，从CONST更改为TYPE）
		// 或者下一个声明有相关的文档，
		// 打印顶级声明之间的空行。
		// （因为p.linebreak是用d的位置调用的
		// 如果没有任何文件，则满足最低要求
		// 即使没有额外的getDoc（d）nil检查，也要保留它以防
		// 换行逻辑改进-已经有一个待办事项）。
		if len(p.output) > 0 {
			// 只有在不在输出开始时才打印换行符
			// （即，我们不仅打印部分程序）
			min := 1
			if prev != tok || getDoc(d) != nil {
				min = 2
			}
			// 如果下一个声明是函数，则启动新节
			// 跨越多条线（另见第19544期）
			p.linebreak(p.lineFor(d.Pos()), min, ignore, tok == token.FUNC && p.numLines(d) > 1)
		}
		p.decl(d)
	}
}

func (p *printer) file(src *ast.File) {
	p.setComment(src.Doc)
	p.print(src.Pos(), token.PACKAGE, blank)
	p.expr(src.Name)
	p.declList(src.Decls)
	p.print(newline)
}
